#include "RpcProcessor.h"


namespace zbus { 
	 
	RpcProcessor::RpcProcessor(int threadPoolSize) :threadPool(threadPoolSize) {

	} 
	void RpcProcessor::addMethod(std::string module, std::string methodName, MethodJJ method) {
		std::unique_lock<std::mutex> lock(methodMutex);
		Method m;
		m.jj = method;
		m.type = Method::JJ;
		this->methodTable[module + ":" + methodName] = m;  
	}

	void RpcProcessor::addMethod(std::string module, std::string methodName, MethodJJM method) {
		std::unique_lock<std::mutex> lock(methodMutex);
		Method m;
		m.jjm = method;
		m.type = Method::JJM;
		this->methodTable[module + ":" + methodName] = m;
	}

	void RpcProcessor::addMethod(std::string module, std::string methodName, MethodMJ method) {
		std::unique_lock<std::mutex> lock(methodMutex);
		Method m;
		m.mj = method;
		m.type = Method::MJ;
		this->methodTable[module + ":" + methodName] = m;
	}
	void RpcProcessor::addMethod(std::string module, std::string methodName, MethodMJM method) {
		std::unique_lock<std::mutex> lock(methodMutex);
		Method m;
		m.mjm = method;
		m.type = Method::MJM;
		this->methodTable[module + ":" + methodName] = m;
	}
	void RpcProcessor::addMethod(std::string module, std::string methodName, MethodMM method) {
		std::unique_lock<std::mutex> lock(methodMutex);
		Method m;
		m.mm = method;
		m.type = Method::MM;
		this->methodTable[module + ":" + methodName] = m;
	}
	void RpcProcessor::addMethod(std::string module, std::string methodName, MethodM method) {
		std::unique_lock<std::mutex> lock(methodMutex);
		Method m;
		m.m = method;
		m.type = Method::M;
		this->methodTable[module + ":" + methodName] = m;
	}

	Method* RpcProcessor::findMethod(Request* req) {
		std::unique_lock<std::mutex> lock(methodMutex);
		std::string fullName = req->module + ":" + req->method;
		auto iter = methodTable.find(fullName);
		if (iter != methodTable.end()) {
			return &iter->second;
		}
		return NULL;
	}

	void RpcProcessor::handleAsync(Message* reqMsg, MqClient* client) {
		threadPool.submit([this, reqMsg, client]() {
			this->handle(reqMsg, client);
		});
	}

	void RpcProcessor::handle(Message* reqMsg, MqClient* client) {
		Message* resMsg = NULL;
		std::string msgId = reqMsg->getId();
		std::string topic = reqMsg->getTopic();
		std::string recver = reqMsg->getSender(); 

		Request req; 
		Json::Value res;
		std::string status = "200";
		try {
			std::string body = reqMsg->getBodyString();
			req.fromJson(body);
			if (req.module == "") {
				req.module = "index";
			}
			if (req.method == "") {
				req.method = "index";
			} 

			
			Method* m = findMethod(&req);  
			if (m == NULL) {
				status = "600";
				res = Json::Value("Missing method: " + req.module + ":" + req.method); 
			} else {
				if (m->type == Method::JJ) {
					res = m->jj(req.params);
				} else if (m->type == Method::JJM) {
					res = m->jjm(req.params, reqMsg);
				} else if (m->type == Method::MJ) {
					resMsg = m->mj(req.params);
				} else if (m->type == Method::MJM) {
					resMsg = m->mjm(req.params, reqMsg);
				} else if (m->type == Method::MM) {
					resMsg = m->mm(reqMsg);
				} else if (m->type == Method::M) {
					resMsg = m->m();
				} else {
					//Should not run here!
				}
			} 
		} catch (std::exception& e) {
			status = "600";
			res = Json::Value(e.what());
		}

		if (resMsg == NULL) {
			resMsg = new Message(); 
			Json::StyledWriter writer;
			resMsg->setJsonBody(writer.write(res));
			resMsg->status = status;
		}  

		resMsg->setId(msgId);
		resMsg->setTopic(topic);
		resMsg->setRecver(recver); 
		
		client->route(*resMsg);

		delete reqMsg;
		delete resMsg;
	}  
}